#include "Node.h"
#include <iostream>
#include "Mailer.h"

Node::Node(vector<int> neighborsName, Mailer* mailer) :	OSThread() {

	_Id = neighborsName[0];
	neighborsName.erase(neighborsName.begin()); //remove first element (ID)
	_neighbors = neighborsName; //getting neighbors
	_mailer = mailer;
	_inbox = new UnBoundMailBox(_Id);
	_yield = false;
	//printing success
	printVector(_neighbors);
	//cout << endl;
	// new attemo for finding if stable
	_stable = false;
	_stepStableCounter = 0;
	_continue=true;


}
Node::~Node() {

}

/* studentSend - an abstract method that is used to send the message you wish to send */
bool Node::studentSend(void *args)
{
	pthread_mutex_t mutex = _mailer->getMailboxMutex();
	pthread_cond_t cond = _mailer->getMailboxCondVar();

	//catch Mutex
	pthread_mutex_lock(&mutex);

	while(_mailer->fullInbox())
	{
		//wait on condition variable:
		pthread_cond_wait(&cond, &mutex);

	}
	//ADD TO MAILBOX
	_mailer->addLast((Message*)args);
	pthread_mutex_unlock(&mutex);

	//cout<<"Node: inside student send!!"<< endl;
	//_mailer->addLast((Message*)args);
	return true;
}

/* studentRecv - an abstract method that is used to receive a message */
bool Node::studentRecv(void **args)
{
	bool changed = false;
	Message *m = _inbox->getNextMsg();
	string type = m->getMsgType();
	//cout << "Node: MSG type: " << type << endl;
	if (type.compare("rt") == 0)
	{
		//cout << "Node: INSIDE IF => RT" << endl;
		RT* rtmsg = m->getRtMsg();
		changed = _rt->update(rtmsg);
	//	cout << "changed? " << changed << endl;
	}
	else if (type.compare("system") == 0)
	{
	/*	string command = m->getSysMsg()->getCommand();
		if(command.compare("kill") == 0)
		{
			_continue = false;

		}*/
	}
	return changed;
}

int Node::getNeighborsNumber() {
	return _neighbors.size();
}

void Node::addLast(Message* msg)
{
	//cout<<"Node: adding msg to the end of the queue."<< endl;
	_inbox->addLast(msg);
}

void Node::addFirst(Message* msg)
{
	_inbox->addFirst(msg);
}

void Node::FirstSend()
{
	for (int i = 0; i < _neighbors.size(); i++)
	{
		int sendTo = _neighbors[i];
		//cout << "Node: node " << _Id << " sending RT to neighbor " << sendTo << endl;
		Message* newMsg = new Message(_Id, sendTo, _rt);
		//_mailer->addLast(newMsg);
		//cout<<"Node: HERE AFTER MSG CREATED"<<endl;
		send(newMsg);
	}
}

/* takes a RT messages from inbox, and updates current RT.
 * if mailbox is empty, sends RT message to neigbors
 * called knowing next msg is an RT
 */
void Node::run()
{

	FirstSend();
	//bool changed = false;
	_continue = true;
	while (_continue)
	{
		bool changed = false;
		while (!_inbox->isEmpty())
		{
			_yield = false;
			bool res = recv(NULL);
			//cout<<"Node: res is = " << res << endl;
			if( res )
			{
				changed = true;
				//cout<<"Node: node "<<_Id<<" printing his new RT"<< endl;
				PrintRT();
			}
		}

		if (changed)
		{
			// send RT to neighbors
			for (int i = 0; i < _neighbors.size(); i++)
			{
						int sendTo = _neighbors[i];
						//cout << "Node: node " << _Id << " sending RT to neighbor " << sendTo << endl;
						Message* newMsg = new Message(_Id, sendTo, _rt);
						//_mailer->addLast(newMsg);
						send(newMsg);
			}
		}

		if (changed){
//			cout<<"= = = =NODE "<< _Id<< "is Changed = = = ="<< endl;
			_stepStableCounter = 0;
		}
		else{
			_stepStableCounter++;
			if (_stepStableCounter > _rt->getSize()*2){
				_stable = true;
			}
//			cout<<"= = = =NODE "<< _Id<< "is NOT Changed = = = ="<< endl;
		}
		_yield = true;
		yield();
		//_continue = false;
	}
}

bool Node::isYield()
{
	return _yield;
}
void Node::stop() {
	_continue = false;
}

//=================  Auxilary  =============================
bool Node::isStable(){
	return _stable;
}
bool Node::IsActive(){
	return _continue;
}

// true if phase%2 = 1
bool Node::isBlocked(){
	return _mailer->fullInbox();
}

int Node::getId() {
	return _Id;
}
vector<int> Node::getNeighbors() {
	return _neighbors;
}

void Node::printNeighbors() {
	vector<int>::iterator it;
	for (it = _neighbors.begin(); it < _neighbors.end(); it++) {
		cout << " " << *it;
	}
	cout << endl;
}

RT* Node::getRT() {
	return _rt;
}

void Node::PrintRT() {
	_rt->printRT();
}

void Node::printVector(vector<int> v) {
	vector<int>::iterator it;
	for (it = v.begin(); it < v.end(); it++) {
		cout << " " << *it;
	}
	cout << endl;
}

//=================  Initialization =============================
//required: vector
void Node::InitRT(vector<int> nodesInGraph)
{
//	cout << "Node: node " << _Id << " is initializing RT " << endl;
	_rt = new RT(nodesInGraph, _Id);

	//checking built properly
	//_rt->printRT();
}

void Node::revive()
{
	_rt->Reset();
	_inbox->clear();
	FirstSend();
}

int Node::getInboxSize(){
	return _inbox->getSize();
}
